/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.geo.map;

import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import technotheist.geo.map.MapUV;
import technotheist.geo.map.vector.Float3Map;

/**
 *
 * @author Mitchel Strachan
 */
public final class MapUtil {

    
    
    private MapUtil() {
    }
    public static final int HEX = 6, PENT = 5;
    public static final int PENT_COUNT = 12;
    public static final int NW = 0, WW = 1, SW = 2, SE = 3, EE = 4, NE = 5;
    public static final int PANELS = 10;
    public static final int P_SUB1 = 9;
    public static final int P_SUB2 = 8;
    public static final float NTH_X = 0;
    public static final float NTH_Y = 1;
    public static final float NTH_Z = 0;
    public static final float STH_X = 0;
    public static final float STH_Y = -1;
    public static final float STH_Z = 0;
    //
    public static final float NV0_X = (float) (Math.sin(Math.toRadians(60)) * Math.cos(Math.toRadians(0)));
    public static final float NV0_Y = (float) (Math.sin(Math.toRadians(60)) * Math.sin(Math.toRadians(0)));
    public static final float NVn_Z = (float) Math.cos(Math.toRadians(60));
    //
    public static final float NV1_X = (float) (Math.sin(Math.toRadians(60)) * Math.cos(Math.toRadians(72)));
    public static final float NV1_Y = (float) (Math.sin(Math.toRadians(60)) * Math.sin(Math.toRadians(72)));
    //
    public static final float NV2_X = (float) (Math.sin(Math.toRadians(60)) * Math.cos(Math.toRadians(144)));
    public static final float NV2_Y = (float) (Math.sin(Math.toRadians(60)) * Math.sin(Math.toRadians(144)));
    //
    public static final float NV3_X = (float) (Math.sin(Math.toRadians(60)) * Math.cos(Math.toRadians(216)));
    public static final float NV3_Y = (float) (Math.sin(Math.toRadians(60)) * Math.sin(Math.toRadians(216)));
    //
    public static final float NV4_X = (float) (Math.sin(Math.toRadians(60)) * Math.cos(Math.toRadians(288)));
    public static final float NV4_Y = (float) (Math.sin(Math.toRadians(60)) * Math.sin(Math.toRadians(288)));
    //
    public static final float SV0_X = (float) (Math.sin(Math.toRadians(120)) * Math.cos(Math.toRadians(36)));
    public static final float SV0_Y = (float) (Math.sin(Math.toRadians(120)) * Math.sin(Math.toRadians(36)));
    public static final float SVn_Z = (float) Math.cos(Math.toRadians(120));
    //
    public static final float SV1_X = (float) (Math.sin(Math.toRadians(120)) * Math.cos(Math.toRadians(108)));
    public static final float SV1_Y = (float) (Math.sin(Math.toRadians(120)) * Math.sin(Math.toRadians(108)));
    //
    public static final float SV2_X = (float) (Math.sin(Math.toRadians(120)) * Math.cos(Math.toRadians(180)));
    public static final float SV2_Y = (float) (Math.sin(Math.toRadians(120)) * Math.sin(Math.toRadians(180)));
    //
    public static final float SV3_X = (float) (Math.sin(Math.toRadians(120)) * Math.cos(Math.toRadians(252)));
    public static final float SV3_Y = (float) (Math.sin(Math.toRadians(120)) * Math.sin(Math.toRadians(252)));
    //
    public static final float SV4_X = (float) (Math.sin(Math.toRadians(120)) * Math.cos(Math.toRadians(324)));
    public static final float SV4_Y = (float) (Math.sin(Math.toRadians(120)) * Math.sin(Math.toRadians(324)));

    public static Vector3f NORTH() {
        return new Vector3f(NTH_X, NTH_Y, NTH_Z);
    }

    public static Vector3f N0() {
        return new Vector3f(NV0_X, NV0_Y, NVn_Z);
    }

    public static Vector3f N1() {
        return new Vector3f(NV1_X, NV1_Y, NVn_Z);
    }

    public static Vector3f N2() {
        return new Vector3f(NV2_X, NV2_Y, NVn_Z);
    }

    public static Vector3f N3() {
        return new Vector3f(NV3_X, NV3_Y, NVn_Z);
    }

    public static Vector3f N4() {
        return new Vector3f(NV4_X, NV4_Y, NVn_Z);
    }

    public static Vector3f SOUTH() {
        return new Vector3f(STH_X, STH_Y, STH_Z);
    }

    public static Vector3f S0() {
        return new Vector3f(SV0_X, SV0_Y, SVn_Z);
    }

    public static Vector3f S1() {
        return new Vector3f(SV1_X, SV1_Y, SVn_Z);
    }

    public static Vector3f S2() {
        return new Vector3f(SV2_X, SV2_Y, SVn_Z);
    }

    public static Vector3f S3() {
        return new Vector3f(SV3_X, SV3_Y, SVn_Z);
    }

    public static Vector3f S4() {
        return new Vector3f(SV4_X, SV4_Y, SVn_Z);
    }
    public static final float FOUR_PI = FastMath.PI * 4;
    public static final float FOUR_PI_OVER_THREE = FOUR_PI / 3;

    public static float SphereArea(float radius) {
        return FOUR_PI * radius * radius;
    }

    public static float SphereVolume(float radius) {
        return FOUR_PI_OVER_THREE * radius * radius * radius;
    }

    public static int Index(int p, int u, int v, int sideLength) {
        while (u < 0 || u >= sideLength || v < 0 || v >= sideLength) {
            if ((p & 1) == 0) {
                if (u == 0 && v == sideLength) {
                    return PANELS * sideLength * sideLength + 1;
                }
                if (u < 0) {
                    int tmp = v;
                    v = sideLength + u;
                    u = tmp;
                    p = (p + P_SUB2) % PANELS;
                } else if (u >= sideLength) {
                    u -= sideLength;
                    p = (p + 1) % PANELS;
                } else if (v < 0) {
                    v += PANELS;
                    p = (p + P_SUB1) % PANELS;
                } else if (v >= sideLength) {
                    int tmp = u;
                    u = v - sideLength;
                    v = sideLength - tmp;
                    p = (p + 2) % PANELS;
                }
            } else {
                if (v == 0 && u == sideLength) {
                    return PANELS * sideLength * sideLength;
                }
                if (u < 0) {
                    u += sideLength;
                    p = (p + P_SUB1) % PANELS;
                } else if (u >= sideLength) {
                    int tmp = v;
                    v = u - sideLength;
                    u = sideLength - tmp;
                    p = (p + 2) % PANELS;
                } else if (v < 0) {
                    int tmp = u;
                    u = v + sideLength;
                    v = sideLength - tmp;
                    p = (p + P_SUB2) % PANELS;
                } else if (v >= sideLength) {
                    v -= sideLength;
                    p = (p + 1) % PANELS;
                }
            }
        }
        return u + sideLength * (v + sideLength * p);
    }

    public static int[] PentIndices(int sideLength) {
        int elementCount = sideLength * sideLength * PANELS + 2;
        return new int[]{
            elementCount - 1, elementCount - 2,
            Index(0, 0, 0, sideLength),
            Index(1, 0, 0, sideLength),
            Index(2, 0, 0, sideLength),
            Index(3, 0, 0, sideLength),
            Index(4, 0, 0, sideLength),
            Index(5, 0, 0, sideLength),
            Index(6, 0, 0, sideLength),
            Index(7, 0, 0, sideLength),
            Index(8, 0, 0, sideLength),
            Index(9, 0, 0, sideLength)
        };
    }

    public static int[][] PentNeighbours(int sideLength) {
        return new int[][]{
            {
                Index(0, 0, sideLength - 1, sideLength),
                Index(2, 0, sideLength - 1, sideLength),
                Index(4, 0, sideLength - 1, sideLength),
                Index(6, 0, sideLength - 1, sideLength),
                Index(8, 0, sideLength - 1, sideLength),},
            {
                Index(1, sideLength - 1, 0, sideLength),
                Index(3, sideLength - 1, 0, sideLength),
                Index(5, sideLength - 1, 0, sideLength),
                Index(7, sideLength - 1, 0, sideLength),
                Index(9, sideLength - 1, 0, sideLength),},
            //
            {
                Index(8, sideLength - 1, sideLength - 1, sideLength),
                Index(9, 0, sideLength - 1, sideLength),
                Index(0, 1, 0, sideLength),
                Index(0, 1, 1, sideLength),
                Index(0, 0, 1, sideLength),},
            {
                Index(0, sideLength - 1, 0, sideLength),
                Index(9, sideLength - 1, sideLength - 1, sideLength),
                Index(1, 1, 0, sideLength),
                Index(1, 1, 1, sideLength),
                Index(1, 0, 1, sideLength),},
            //
            {
                Index(0, sideLength - 1, sideLength - 1, sideLength),
                Index(1, 0, sideLength - 1, sideLength),
                Index(2, 1, 0, sideLength),
                Index(2, 1, 1, sideLength),
                Index(2, 0, 1, sideLength),},
            {
                Index(2, sideLength - 1, 0, sideLength),
                Index(1, sideLength - 1, sideLength - 1, sideLength),
                Index(3, 1, 0, sideLength),
                Index(3, 1, 1, sideLength),
                Index(3, 0, 1, sideLength),},
            //
            {
                Index(2, sideLength - 1, sideLength - 1, sideLength),
                Index(3, 0, sideLength - 1, sideLength),
                Index(4, 1, 0, sideLength),
                Index(4, 1, 1, sideLength),
                Index(4, 0, 1, sideLength),},
            {
                Index(4, sideLength - 1, 0, sideLength),
                Index(3, sideLength - 1, sideLength - 1, sideLength),
                Index(5, 1, 0, sideLength),
                Index(5, 1, 1, sideLength),
                Index(5, 0, 1, sideLength),},
            //
            {
                Index(4, sideLength - 1, sideLength - 1, sideLength),
                Index(5, 0, sideLength - 1, sideLength),
                Index(6, 1, 0, sideLength),
                Index(6, 1, 1, sideLength),
                Index(6, 0, 1, sideLength),},
            {
                Index(6, sideLength - 1, 0, sideLength),
                Index(5, sideLength - 1, sideLength - 1, sideLength),
                Index(7, 1, 0, sideLength),
                Index(7, 1, 1, sideLength),
                Index(7, 0, 1, sideLength),},
            //
            {
                Index(6, sideLength - 1, sideLength - 1, sideLength),
                Index(7, 0, sideLength - 1, sideLength),
                Index(8, 1, 0, sideLength),
                Index(8, 1, 1, sideLength),
                Index(8, 0, 1, sideLength),},
            {
                Index(8, sideLength - 1, 0, sideLength),
                Index(7, sideLength - 1, sideLength - 1, sideLength),
                Index(9, 1, 0, sideLength),
                Index(9, 1, 1, sideLength),
                Index(9, 0, 1, sideLength),}
        };
    }

    public static void IndexToPos(int idx, int sideLength, MapPos store) {
        int pArea = sideLength * sideLength;
        int mArea = pArea * PANELS;
        if (idx == mArea + 1) {
            store.panel = 0;
            store.u = 0;
            store.v = 1;
            return;
        }
        if (idx == mArea) {
            store.panel = 1;
            store.u = 1;
            store.v = 0;
            return;
        }
        store.panel = (byte) (idx/pArea);
        int pi = idx%pArea;
        int u = pi%sideLength;
        int v = pi/sideLength;
        store.u = u;
        store.v = v;
    }

    public static void IndexToUV(int idx, int sideLength, MapUV store) {
        int pArea = sideLength * sideLength;
        int mArea = pArea * PANELS;
        if (idx == mArea + 1) {
            store.panel = 0;
            store.u = 0;
            store.v = 1;
            return;
        }
        if (idx == mArea) {
            store.panel = 1;
            store.u = 1;
            store.v = 0;
            return;
        }
        store.panel = (byte) (idx/pArea);
        int pi = idx%pArea;
        int u = pi%sideLength;
        int v = pi/sideLength;
        store.u = ((float)u)/sideLength;
        store.v = ((float)v)/sideLength;
    }
    
    
    
    
    public static void initUnit(Float3Map data) {
        Vector3f[] vecs = new Vector3f[9];
        for (int i = 0; i < 9; i++) {
            vecs[i] = new Vector3f();
        }
        data.set(0, 0, data.sideLength, MapUtil.NORTH());
        data.set(1, data.sideLength, 0, MapUtil.SOUTH());
        //
        data.set(0, 0, 0, MapUtil.N0());
        data.set(2, 0, 0, MapUtil.N1());
        data.set(4, 0, 0, MapUtil.N2());
        data.set(6, 0, 0, MapUtil.N3());
        data.set(8, 0, 0, MapUtil.N4());
        //
        data.set(1, 0, 0, MapUtil.S0());
        data.set(3, 0, 0, MapUtil.S1());
        data.set(5, 0, 0, MapUtil.S2());
        data.set(7, 0, 0, MapUtil.S3());
        data.set(9, 0, 0, MapUtil.S4());
        //
        for (int i = 0; i < MapUtil.PANELS; i++) {
            initUnit(i, 0, 0, data.sideLength, vecs, data);
        }
    }
    
    
    private static final int _W = 0, _SW = 1, _S = 2, _NW = 3, _C = 4, _SE = 5, _N = 6, _NE = 7, _E = 8;
    private static void initUnit(int panel, int u, int v, int len, Vector3f[] vecs, Float3Map data) {
        int hl = len / 2;
        data.get(panel, u, v, vecs[_W]);
        data.get(panel, u + len, v, vecs[_S]);
        data.get(panel, u, v + len, vecs[_E]);
        data.get(panel, u + len, v + len, vecs[_N]);
        vecs[_SW].set(vecs[_S]).addLocal(vecs[_W]).normalizeLocal();
        vecs[_SE].set(vecs[_S]).addLocal(vecs[_E]).normalizeLocal();
        vecs[_NW].set(vecs[_N]).addLocal(vecs[_W]).normalizeLocal();
        vecs[_NE].set(vecs[_N]).addLocal(vecs[_E]).normalizeLocal();
        data.set(panel, u + hl, v, vecs[_SW]);
        data.set(panel, u, v + hl, vecs[_NW]);
        data.set(panel, u + len, v + hl, vecs[_SE]);
        data.set(panel, u + hl, v + len, vecs[_NE]);
    }
}
